﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Unis.Framework.Core.Exceptions;
using Unis.Framework.ORM.Emit;
using Unis.ORM.Common;

namespace Unis.Framework.ORM.Common
{
    public class DataRelationContainer<TBaseModel> : IDataRelationContainer<TBaseModel>
    {
        // Fields
        private Dictionary<TBaseModel, Dictionary<Type, List<TBaseModel>>> _Childs;
        private static Dictionary<Type, Dictionary<string, DynamicPropertyAccessor>> _DynamicAccess;
        private static readonly string DEFAULT_KEY_NAME;
        private static Dictionary<Type, EntityRelationInfo[]> RELATION_DATA_TYPE;

        // Methods
        static DataRelationContainer()
        {
            DataRelationContainer<TBaseModel>.RELATION_DATA_TYPE = new Dictionary<Type, EntityRelationInfo[]>();
            DataRelationContainer<TBaseModel>._DynamicAccess = new Dictionary<Type, Dictionary<string, DynamicPropertyAccessor>>();
            DataRelationContainer<TBaseModel>.DEFAULT_KEY_NAME = "ID";
        }

        public DataRelationContainer(TBaseModel rootEntity)
        {
            this._Childs = new Dictionary<TBaseModel, Dictionary<Type, List<TBaseModel>>>();
            this.RootEntity = rootEntity;
        }

        public void AddItem(DataRelationValue<TBaseModel> val)
        {
            this.checkAllowAdd(val);
            Dictionary<Type, List<TBaseModel>> dictionary = null;
            if (this._Childs.ContainsKey(val.Parent))
            {
                dictionary = this._Childs[val.Parent];
            }
            else
            {
                dictionary = new Dictionary<Type, List<TBaseModel>>();
                this._Childs.Add(val.Parent, dictionary);
            }
            Type key = val.Child.GetType();
            List<TBaseModel> list = null;
            if (dictionary.ContainsKey(key))
            {
                list = dictionary[key];
            }
            else
            {
                list = new List<TBaseModel>();
                dictionary.Add(key, list);
            }
            list.Add(val.Child);
        }

        private int checkAllowAdd(DataRelationValue<TBaseModel> val)
        {
            if (val.Parent == null)
            {
                throw new APPException("父对象输入不能为空");
            }
            if (val.Child == null)
            {
                throw new APPException("添加的子对象不能为空");
            }
            Type key = val.Parent.GetType();
            if (!DataRelationContainer<TBaseModel>.RELATION_DATA_TYPE.ContainsKey(key))
            {
                throw new APPException(string.Format("父对象类型 {0} 还没有进行注册", key.FullName));
            }
            EntityRelationInfo[] infoArray = DataRelationContainer<TBaseModel>.RELATION_DATA_TYPE[key];
            Type childType = val.Child.GetType();
            if (Enumerable.FirstOrDefault<EntityRelationInfo>(infoArray, (Func<EntityRelationInfo, bool>)(o => (o.EntityType == childType))) == null)
            {
                throw new APPException(string.Format("父对象类型{0} 下的子类型{1} 还没有进行注册", key.FullName, childType.FullName));
            }
            return 1;
        }

        public TDetail[] GetAllChilds<TDetail>() where TDetail : TBaseModel
        {
            Action<TBaseModel> action = null;
            List<TDetail> lstData = new List<TDetail>();
            Type key = typeof(TDetail);
            foreach (Dictionary<Type, List<TBaseModel>> dictionary in this._Childs.Values)
            {
                if (dictionary.ContainsKey(key))
                {
                    if (action == null)
                    {
                        action = delegate(TBaseModel o)
                        {
                            lstData.Add((TDetail)o);
                        };
                    }
                    dictionary[key].ForEach(action);
                }
            }
            return lstData.ToArray();
        }

        public int GetAllChildsCount<TDetail>() where TDetail : TBaseModel
        {
            int num = 0;
            Type key = typeof(TDetail);
            foreach (Dictionary<Type, List<TBaseModel>> dictionary in this._Childs.Values)
            {
                if (dictionary.ContainsKey(key))
                {
                    num += dictionary[key].Count;
                }
            }
            return num;
        }

        public TDetail[] GetChilds<T, TDetail>(T parent)
            where T : TBaseModel
            where TDetail : TBaseModel
        {
            if (!this._Childs.ContainsKey(parent))
            {
                return new TDetail[0];
            }
            Dictionary<Type, List<TBaseModel>> dictionary = this._Childs[parent];
            if (!dictionary.ContainsKey(typeof(TDetail)))
            {
                return new TDetail[0];
            }
            return dictionary[typeof(TDetail)].ConvertAll<TDetail>(o => (TDetail)o).ToArray();
        }

        private static void iniDataTypeProAccess(Type dataType, string proName)
        {
            Dictionary<string, DynamicPropertyAccessor> dictionary = null;
            if (DataRelationContainer<TBaseModel>._DynamicAccess.ContainsKey(dataType))
            {
                dictionary = DataRelationContainer<TBaseModel>._DynamicAccess[dataType];
            }
            else
            {
                dictionary = new Dictionary<string, DynamicPropertyAccessor>();
                DataRelationContainer<TBaseModel>._DynamicAccess.Add(dataType, dictionary);
            }
            if (!dictionary.ContainsKey(proName))
            {
                PropertyInfo property = dataType.GetProperty(proName);
                if (property == null)
                {
                    throw new APPException(string.Format("实体类型{0} 中不包含键名称 {1}", dataType.FullName, proName), APPMessageType.SysErrInfo);
                }
                DynamicPropertyAccessor accessor = new DynamicPropertyAccessor(dataType, property);
                dictionary.Add(property.Name, accessor);
            }
        }

        private static void iniTypeDynamicAccess(Type parentType, Type childType, EntityRelationMapInfo relation)
        {
            DataRelationContainer<TBaseModel>.iniDataTypeProAccess(parentType, DataRelationContainer<TBaseModel>.DEFAULT_KEY_NAME);
            DataRelationContainer<TBaseModel>.iniDataTypeProAccess(childType, DataRelationContainer<TBaseModel>.DEFAULT_KEY_NAME);
            if (relation != null)
            {
                DataRelationContainer<TBaseModel>.iniDataTypeProAccess(parentType, relation.KeyName);
                DataRelationContainer<TBaseModel>.iniDataTypeProAccess(childType, relation.ForeingKeyName);
            }
        }

        public static void RegisterRelation<T, TDetail>(Expression<Func<TDetail, object>> foreingKeyNameExpress)
            where T : TBaseModel
            where TDetail : TBaseModel
        {
            DataRelationContainer<TBaseModel>.RegisterRelation<T, TDetail>(new EntityRelationMapInfo(ExpressionHelper.GetPropertyName<TDetail>(foreingKeyNameExpress), DataRelationContainer<TBaseModel>.DEFAULT_KEY_NAME));
        }

        private static void RegisterRelation<T, TDetail>(EntityRelationMapInfo relation)
            where T : TBaseModel
            where TDetail : TBaseModel
        {
            Type key = typeof(T);
            Type entity = typeof(TDetail);
            if (!DataRelationContainer<TBaseModel>.RELATION_DATA_TYPE.ContainsKey(key))
            {
                DataRelationContainer<TBaseModel>.RELATION_DATA_TYPE.Add(key, new EntityRelationInfo[] { new EntityRelationInfo(entity, relation) });
            }
            else
            {
                DataRelationContainer<TBaseModel>.RELATION_DATA_TYPE[key] = new List<EntityRelationInfo>(DataRelationContainer<TBaseModel>.RELATION_DATA_TYPE[key]) { new EntityRelationInfo(entity, relation) }.ToArray();
            }
            DataRelationContainer<TBaseModel>.iniTypeDynamicAccess(key, entity, relation);
        }

        public static void RegisterRelation<T, TDetail>(string foreingKeyName)
            where T : TBaseModel
            where TDetail : TBaseModel
        {
            DataRelationContainer<TBaseModel>.RegisterRelation<T, TDetail>(new EntityRelationMapInfo(foreingKeyName, DataRelationContainer<TBaseModel>.DEFAULT_KEY_NAME));
        }

        private void resetChildForingKeyValue(TBaseModel entity)
        {
            Type type = entity.GetType();
            int num = (int)DataRelationContainer<TBaseModel>._DynamicAccess[type][DataRelationContainer<TBaseModel>.DEFAULT_KEY_NAME].Get(entity);
            if (this._Childs.ContainsKey(entity))
            {
                using (Dictionary<Type, List<TBaseModel>>.KeyCollection.Enumerator enumerator = this._Childs[entity].Keys.GetEnumerator())
                {
                    Func<EntityRelationInfo, bool> func = null;
                    Type childType;
                    while (enumerator.MoveNext())
                    {
                        childType = enumerator.Current;
                        if (func == null)
                        {
                            func = o => o.EntityType == childType;
                        }
                        EntityRelationInfo info = Enumerable.FirstOrDefault<EntityRelationInfo>(DataRelationContainer<TBaseModel>.RELATION_DATA_TYPE[type], func);
                        DynamicPropertyAccessor accessor = DataRelationContainer<TBaseModel>._DynamicAccess[childType][info.RelationMap.ForeingKeyName];
                        foreach (TBaseModel local in this._Childs[entity][childType])
                        {
                            accessor.Set(local, num);
                            this.resetChildForingKeyValue(local);
                        }
                    }
                }
            }
        }

        public void ResetForeingKeyValue()
        {
            this.resetChildForingKeyValue(this.RootEntity);
        }

        public void SetAllChildsID<TDetail>(int id) where TDetail : TBaseModel
        {
            Action<TBaseModel> action = null;
            Type key = typeof(TDetail);
            DynamicPropertyAccessor keyname = DataRelationContainer<TBaseModel>._DynamicAccess[key][DataRelationContainer<TBaseModel>.DEFAULT_KEY_NAME];
            foreach (Dictionary<Type, List<TBaseModel>> dictionary in this._Childs.Values)
            {
                if (dictionary.ContainsKey(key))
                {
                    if (action == null)
                    {
                        action = delegate(TBaseModel o)
                        {
                            keyname.Set(o, id++);
                        };
                    }
                    dictionary[key].ForEach(action);
                }
            }
        }

        // Properties
        public TBaseModel RootEntity
        {
            get;
            set;
            //    [CompilerGenerated]
            //    get
            //{
            //    return this.<RootEntity>k__BackingField;
            //}
            //    [CompilerGenerated]
            //    private set
            //{
            //    this.<RootEntity>k__BackingField = value;
            //}
        }
    }
}
